declare var document: Document;

declare class RouteRegExp extends RegExp {
    keys: Array<{ name: string, optional: boolean }>;
}

//这里的选项其实就是path-to-regexp框架的选项
declare type PathToRegexpOptions = {
    sensitive?: boolean,
    //在处理当前route时，默认情况下会将path字符串最后可能存在的'/'替换为空字符串，
    //如果设置strict属性为true，则会保留该字符。
    strict?: boolean,
    end?: boolean
}

declare module 'path-to-regexp' {
    declare var exports: {
        (path: string, keys?: Array<?{ name: string }>, options?: PathToRegexpOptions): RouteRegExp;
        compile: (path: string) => (params: Object) => string;
    }
}

declare type Dictionary<T> = { [key: string]: T }

declare type NavigationGuard = (to: Route,
                                from: Route,
                                next: (to?: RawLocation | false | Function | void) => void) => any

declare type AfterNavigationHook = (to: Route, from: Route) => any

type Position = { x: number, y: number };
type PositionResult = Position | { selector: string, offset?: Position } | void;

declare type RouterOptions = {
    routes?: Array<RouteConfig>;
    mode?: string;
    //当浏览器不支持 history.pushState 控制路由是否应该回退到 hash 模式。默认值为 true。
    fallback?: boolean;
    base?: string;
    linkActiveClass?: string;
    parseQuery?: (query: string) => Object;
    stringifyQuery?: (query: Object) => string;
    scrollBehavior?: (to: Route,
                      from: Route,
                      savedPosition: ?Position) => PositionResult | Promise<PositionResult>;
}

declare type RedirectOption = RawLocation | ((to: Route) => RawLocation)

//一个路由的配置选项
declare type RouteConfig = {
    path: string;
    name?: string;
    component?: any;
    components?: Dictionary<any>;
    redirect?: RedirectOption;
    //当前路由的别名
    alias?: string | Array<string>;
    children?: Array<RouteConfig>;
    beforeEnter?: NavigationGuard;
    meta?: any;
    props?: boolean | Object | Function;
    caseSensitive?: boolean;
    pathToRegexpOptions?: PathToRegexpOptions;
}

//对RouteConfig配置对象处理后的对象
declare type RouteRecord = {
    /**
     * 在RouteConfig对象中的path属性经过标准化后的路径，也就是完整的path，
     * 示例：
     * {
     *      path:'/a',
     *      children:{
     *          path:'b'
     *      }
     * }
     * 则对于path:'b'来说，其对应的RouteRecord对象的path的值为"/a/b'
     */
    path: string;
    //通过path-to-regexp框架处理path之后的正则表达式
    regex: RouteRegExp;
    components: Dictionary<any>;
    //
    instances: Dictionary<any>;
    name: ?string;
    parent: ?RouteRecord;
    redirect: ?RedirectOption;
    /** @todo 作用是什么？好像是与别名机制有关
     * 猜测：因为与别名机制有关，而别名其实就是一个路由的path的另一个名字，
     * matchAs就用于存储这个真正的path的路径。
     * 示例：
     *  {
     *       path: '/a',
     *       component: A,
     *       alias: '/A',
     *
     *       children:[{
     *          path: 'b',
     *          component:B,
     *          alias:'B'
     *       }]
     *  }
     *  在解析该路由时，会生成六个路由信息，用路径path的值表示分别为：
     *  /a /A /a/b /a/B /A/b /A/B
     *  比如对于path为/A/B时，其matchAs为/A/b，而/A/b的matchAs又为
     *  /a/b，这样就找到了最原始的可供使用的路径
     */
    matchAs: ?string;
    beforeEnter: ?NavigationGuard;
    meta: any;
    props: boolean | Object | Function | Dictionary<boolean | Object | Function>;
}

declare type Location = {
    _normalized?: boolean;
    name?: string;
    path?: string;
    hash?: string;
    query?: Dictionary<string>;
    params?: Dictionary<string>;
    append?: boolean;
    replace?: boolean;
}

declare type RawLocation = string | Location

//路由信息对象
declare type Route = {
    path: string; //将params与RouteRecord对象合成为一个具体的路径之后的字符串形式
    name: ?string;
    hash: string;
    query: Dictionary<string>;
    params: Dictionary<string>;
    fullPath: string; //该属性用于存储完整的路径字符串，通过将path、query、hash合并
    //一个路由信息对象对应于一个RouteRecord对象，而matched数组用于存放该RouteRecord对象
    //及其祖先对象，存放的顺序是最顶层的祖先放在第一的位置，然后依次放置，最后数组的最后放置与
    //当前路由信息对象关联的RouteRecord对象
    matched: Array<RouteRecord>;
    //redirectedFrom到底是什么？
    //redirectedFrom是与重定向有关的属性。redirectedForm与fullPath的意义是相同的，只是依据的location不同
    //示例：
    //'/a' --> '/b' --> '/c'
    //意思是说，'/a'重定向到'/b'，'/b'又重定向到'/c'，在访问'/a'链接时，会创建一个Location对象，
    //最终找到'/c'的RouteRecord之后，redirectedFrom仍然为最初'/a'创建的那个Location对象
    //所以fullPath是通过'/c'对应的Location创建的，而redirectedFrom则是通过'/a'的Location对象创建的，
    //也就是最初提供的Location对象。
    redirectedFrom?: string;
    //RouteRecord中的meta属性
    meta?: any;
}
